/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Description: implement of oem_auth_result_storage
 * Author: Kit Framework group
 * Create: 2020-12-26
 */

#include "oem_auth_result_storage.h"
#include "securec.h"

#include "iot_flash.h"

typedef struct {
    uint32_t file_len;
    uint32_t file_flag;
} file_info;

enum {
    RESET_FILE_INDEX = 0,
    TICKET_FILE_INDEX,
    AUTH_STATS_FILE_INDEX,
    FILE_NUM,
};

static file_info g_file_infos[FILE_NUM] = {0};

#define FILE_REGION_BEGIN 0x1F8000
#define FILE_REGION_END 0x200000

// all files are stored in one flash sector
#define FILE_INFO_SIZE 0x40
#define RESET_FILE_SIZE 0x04
#define TICKET_FILE_SIZE 0x80
#define AUTH_STATS_FILE_SIZE 0x400
#define FILE_INFO_ADDR FILE_REGION_BEGIN
#define RESET_FILE_BYTES_ADDR FILE_REGION_BEGIN + FILE_INFO_SIZE
#define TICKET_FILE_BYTES_ADDR FILE_REGION_BEGIN + FILE_INFO_SIZE + RESET_FILE_SIZE
#define AUTH_STATS_FILE_BYTES_ADDR FILE_REGION_BEGIN + FILE_INFO_SIZE + RESET_FILE_SIZE + TICKET_FILE_SIZE
#define ALL_FILE_SIZE FILE_INFO_SIZE + RESET_FILE_SIZE + TICKET_FILE_SIZE + AUTH_STATS_FILE_SIZE

bool OEMIsOverTemperatureLimit(void)
{
    /*
     * if device has temprature limit while write data,
     * read the temperature through the manufacturer's interface,
     * and return true when the temperature exceeds the limit.
     */
    return false;
}

/*
 * all files are stored in one flash sector.
 * so while read one file, first read all file in sector, return data according to address.
 */
int32_t FlashRead(uint32_t address, uint8_t *data, uint32_t length)
{
    flash_t flash;

    if ((data == NULL) || (length == 0)) {
        printf("data or length parameter invalid\n");
        return -1;
    }

    if (address > (0xFFFFFFFF - length)) { //溢出
        printf("address overflow \n");
        return -1;
    }

    if (address < FILE_REGION_BEGIN || address > FILE_REGION_END) {
        printf("address invalid \n");
        return -1;
    }

    if (((address + length) < FILE_REGION_BEGIN) || ((address + length) > FILE_REGION_END)) {
        printf("address add length invalid \n");
        return -1;
    }

    uint8_t flashData[ALL_FILE_SIZE] = {0};
    device_mutex_lock(RT_DEV_LOCK_FLASH);
    // flash_stream_read return 1 means success.
    if (IoTFlashRead(&flash, FILE_REGION_BEGIN, ALL_FILE_SIZE, flashData) != 1) {
        device_mutex_unlock(RT_DEV_LOCK_FLASH);
        printf("flash_stream_read failed\n");
        return -1;
    }
    device_mutex_unlock(RT_DEV_LOCK_FLASH);
    (void)memcpy_s(data, length, flashData + address - FILE_REGION_BEGIN, length);
    return 0;
}

//extern void flash_erase_dword(u32 address, u32 dword_num);

/*
 * all files are stored in one flash sector.
 * so while write one file, first read all file in sector, cover data according to address.
 */
int32_t FlashWrite(uint32_t address, uint8_t *data, uint32_t length)
{
    flash_t flash;

    if ((data == NULL) || (length == 0)) {
        printf("data or length parameter invalid\n");
        return -1;
    }
    if (address > (0xFFFFFFFF - length)) { //溢出
        printf("address overflow \n");
        return -1;
    }

    if (address < FILE_REGION_BEGIN || address > FILE_REGION_END) {
        printf("address invalid \n");
        return -1;
    }

    if (((address + length) < FILE_REGION_BEGIN) || ((address + length) > FILE_REGION_END)) {
        printf("address add length invalid \n");
        return -1;
    }
    uint8_t flashData[ALL_FILE_SIZE] = {0};
    device_mutex_lock(RT_DEV_LOCK_FLASH);
    // flash_stream_read return 1 means success.
    if (IoTFlashRead(&flash, FILE_REGION_BEGIN, ALL_FILE_SIZE, flashData) != 1) {
        device_mutex_unlock(RT_DEV_LOCK_FLASH);
        printf("flash_stream_read failed\n");
        return -1;
    }
    (void)memcpy_s(flashData + address - FILE_REGION_BEGIN, length, data, length);
    IoTFlashErase(FILE_REGION_BEGIN, (ALL_FILE_SIZE + 3) / 4);
    // flash_stream_write return 1 means success.
    if (IoTFlashWrite(&flash, FILE_REGION_BEGIN, ALL_FILE_SIZE, flashData) != 1) {
        device_mutex_unlock(RT_DEV_LOCK_FLASH);
        printf("flash_stream_write failed\n");
        return -1;
    }

    uint8_t tempFlashData[ALL_FILE_SIZE] = {0};
    // flash_stream_read return 1 means success.
    if (IoTFlashRead(&flash, FILE_REGION_BEGIN, ALL_FILE_SIZE, tempFlashData) != 1) {
        device_mutex_unlock(RT_DEV_LOCK_FLASH);
        printf("flash_stream_read failed\n");
        return -1;
    }

    device_mutex_unlock(RT_DEV_LOCK_FLASH);

    return 0;
}

static void GetFileInfos(void)
{
    (void)FlashRead(FILE_INFO_ADDR, (uint8_t *)g_file_infos, sizeof(file_info) * FILE_NUM);
}

bool OEMIsResetFlagExist(void)
{
    GetFileInfos();
    uint32_t flag = g_file_infos[RESET_FILE_INDEX].file_flag;
    if (flag == 0) {
        printf("The ResetFlag isn't Exist\n");
        return false;
    }
    printf("The ResetFlag is Exist\n");
    return true;
}

int32_t OEMCreateResetFlag(void)
{
    int32_t ret;
    file_info file_tmp = {0, 1};

    ret = FlashWrite(FILE_INFO_ADDR + (RESET_FILE_INDEX * sizeof(file_info)), (uint8_t *)&file_tmp, sizeof(file_info));
    if (ret != 0) {
        printf("Create reset flag failed\n");
        return -1;
    }

    return 0;
}

int32_t OEMDeleteResetFlag(void)
{
    int32_t ret;
    file_info file_tmp = {0, 0};

    ret = FlashWrite(FILE_INFO_ADDR + (RESET_FILE_INDEX * sizeof(file_info)), (uint8_t *)&file_tmp, sizeof(file_info));
    if (ret != 0) {
        printf("Delete reset flag failed\n");
        return -1;
    }

    return 0;
}

bool OEMIsAuthStatusExist(void)
{
    GetFileInfos();
    uint32_t flag = g_file_infos[AUTH_STATS_FILE_INDEX].file_flag;
    if (flag == 0) {
        printf("The Auth Status File isn't Exist\n");
        return false;
    }
    printf("The Auth Status File is Exist\n");
    return true;
}

int32_t OEMWriteAuthStatus(const char *data, uint32_t len)
{
    int32_t ret;
    file_info file_tmp;
    file_tmp.file_flag = 1;
    file_tmp.file_len = len;
    ret = FlashWrite(AUTH_STATS_FILE_BYTES_ADDR, (uint8_t *)data, len);
    if (ret != 0) {
        printf("Write Auth Status failed\n");
        return -1;
    }

    ret = FlashWrite(
        FILE_INFO_ADDR + (AUTH_STATS_FILE_INDEX * sizeof(file_info)), (uint8_t *)&file_tmp, sizeof(file_info));
    if (ret != 0) {
        printf("Write Auth Status Flag failed\n");
        return -1;
    }

    return 0;
}

int32_t OEMReadAuthStatus(char *buffer, uint32_t bufferLen)
{
    GetFileInfos();
    if (bufferLen < g_file_infos[AUTH_STATS_FILE_INDEX].file_len || g_file_infos[AUTH_STATS_FILE_INDEX].file_len == 0) {
        printf("Input length is invalid or Auth Status file isn't exit\n");
        return -1;
    }
    uint8_t buf[bufferLen];
    (void)memset_s(buf, bufferLen, 0, bufferLen);
    int32_t ret = FlashRead(AUTH_STATS_FILE_BYTES_ADDR, (uint8_t *)buf, g_file_infos[AUTH_STATS_FILE_INDEX].file_len);
    if (ret != 0) {
        printf("Read Auth Status failed\n");
        return -1;
    }
    (void)memcpy_s(
        buffer, g_file_infos[AUTH_STATS_FILE_INDEX].file_len, buf, g_file_infos[AUTH_STATS_FILE_INDEX].file_len);
    return 0;
}

int32_t OEMDeleteAuthStatus(void)
{
    file_info file_tmp = {0, 0};
    int32_t ret = FlashWrite(
        FILE_INFO_ADDR + (AUTH_STATS_FILE_INDEX * sizeof(file_info)), (uint8_t *)&file_tmp, sizeof(file_info));
    if (ret != 0) {
        printf("Delete Auth Status failed\n");
        return -1;
    }
    return 0;
}

int32_t OEMGetAuthStatusFileSize(uint32_t *len)
{
    GetFileInfos();
    if (g_file_infos[AUTH_STATS_FILE_INDEX].file_flag == 1) {
        *len = g_file_infos[AUTH_STATS_FILE_INDEX].file_len;
        printf("The Auth Status File Size is %d\n", *len);
        return 0;
    }
    return -1;
}

bool OEMIsTicketExist(void)
{
    GetFileInfos();
    uint32_t flag = g_file_infos[TICKET_FILE_INDEX].file_flag;
    if (flag == 0) {
        printf("The Ticket File isn't Exist\n");
        return false;
    }
    printf("The Ticket File is Exist\n");
    return true;
}

int32_t OEMWriteTicket(const char *data, uint32_t len)
{
    file_info file_tmp = {len, 1};
    int32_t ret = FlashWrite(TICKET_FILE_BYTES_ADDR, (uint8_t *)data, len);
    if (ret != 0) {
        printf("Write Ticket failed\n");
        return -1;
    }

    ret = FlashWrite(FILE_INFO_ADDR + (TICKET_FILE_INDEX * sizeof(file_info)), (uint8_t *)&file_tmp, sizeof(file_info));
    if (ret != 0) {
        printf("Write Ticket Flag failed\n");
        return -1;
    }
    return 0;
}

int32_t OEMReadTicket(char *buffer, uint32_t bufferLen)
{
    GetFileInfos();
    if (bufferLen < g_file_infos[TICKET_FILE_INDEX].file_len || g_file_infos[TICKET_FILE_INDEX].file_len == 0) {
        printf("Input length is invalid or ticket isn't exit\n");
        return -1;
    }
    uint8_t buf[bufferLen + 1];
    (void)memset_s(buf, bufferLen + 1, 0, bufferLen + 1);
    int32_t ret = FlashRead(TICKET_FILE_BYTES_ADDR, (uint8_t *)buf, g_file_infos[TICKET_FILE_INDEX].file_len);
    if (ret != 0) {
        printf("Read Ticket failed\n");
        return -1;
    }
    (void)memcpy_s(buffer, g_file_infos[TICKET_FILE_INDEX].file_len, buf, g_file_infos[TICKET_FILE_INDEX].file_len);

    return 0;
}

int32_t OEMDeleteTicket(void)
{
    file_info file_tmp = {0, 0};

    int32_t ret =
        FlashWrite(FILE_INFO_ADDR + (TICKET_FILE_INDEX * sizeof(file_info)), (uint8_t *)&file_tmp, sizeof(file_info));
    if (ret != 0) {
        printf("Delete Ticket failed\n");
        return -1;
    }

    return 0;
}

int32_t OEMGetTicketFileSize(uint32_t *len)
{
    GetFileInfos();
    if (g_file_infos[TICKET_FILE_INDEX].file_flag == 1) {
        *len = g_file_infos[TICKET_FILE_INDEX].file_len;
        printf("The Ticket File Size is %d\n", *len);
        return 0;
    }
    return -1;
}
